package fun.tan90.easy.binlog.manager.ws;

import cn.hutool.json.JSONUtil;
import fun.tan90.easy.binlog.manager.property.EasyBinlogManagerProperties;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.noear.socketd.SocketD;
import org.noear.socketd.transport.core.Session;
import org.noear.socketd.transport.core.entity.StringEntity;
import org.noear.socketd.transport.core.listener.EventListener;
import org.noear.socketd.transport.server.Server;
import org.noear.socketd.utils.RunUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * description TODO
 * company 铁人科技
 *
 * @author 陈浩杰
 * @date 2023/12/15 9:48
 */
@Slf4j
@Getter
@Component
public class WsServer implements InitializingBean, DisposableBean {

    private static final Map<String, Session> WS_CLIENTS = new HashMap<>();

    private Server wsServer;

    @Resource
    Environment environment;

    @Resource
    EasyBinlogManagerProperties easyBinlogManagerProperties;

    @Override
    public void afterPropertiesSet() throws Exception {
        Integer port = environment.getProperty("server.port", Integer.class, 8080);
        this.wsServer = SocketD.createServer("sd:ws")
                .config(c -> c.port(port + 2))
                .listen(new EventListener()
                        .doOnOpen(session -> {
                            log.info("WsServer doOnOpen {} {}", wsClientId(session), JSONUtil.toJsonStr(session.handshake().paramMap()));
                            registerWsClient(session);
                        })
                        .doOnClose(session -> {
                            log.info("WsServer onClose {} ", wsClientId(session));
                            unRegisterWsClient(session);
                        })
                        .doOnError((session, error) -> {
                            log.error("WsServer onError {} ", wsClientId(session), error);
                        }))
                .start();
    }

    @Override
    public void destroy() throws Exception {
        for (Session session : WS_CLIENTS.values()) {
            RunUtils.runAndTry(session::closeStarting);
        }
        log.info("WsServer is destroying");
        RunUtils.runAndTry(() -> Thread.sleep(5000));
        for (Session session : WS_CLIENTS.values()) {
            RunUtils.runAndTry(session::close);
        }
        wsServer.stop();
        log.info("WsServer destroyed");
    }

    public String wsClientId(Session session) {
        return session.param("wsClientId");
    }

    private void registerWsClient(Session session) throws IOException {
        String wsClientId = wsClientId(session);
        if (WS_CLIENTS.containsKey(wsClientId)) {
            session.close();
        } else {
            WS_CLIENTS.put(wsClientId, session);
        }
    }

    private void unRegisterWsClient(Session session) {
        String wsClientId = wsClientId(session);
        WS_CLIENTS.remove(wsClientId);
    }

    public void broadcast(String event, String message) {
        for (Session session : WS_CLIENTS.values()) {
            try {
                session.send(event, new StringEntity(message));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
